package com.qiguliuxing.dts.core.storage;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import io.minio.BucketExistsArgs;
import io.minio.GetObjectArgs;
import io.minio.ListObjectsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.InsufficientDataException;
import io.minio.errors.InternalException;
import io.minio.errors.InvalidResponseException;
import io.minio.errors.ServerException;
import io.minio.errors.XmlParserException;
import io.minio.messages.Item;

public class MinioStorage implements Storage {

    private String endpoint;
    private String accessKey;
    private String secretKey;
    private String bucketName;
    private String region;

    public void setEndpoint(String endpoint) {
        this.endpoint = endpoint;
    }

    public void setAccessKey(String accessKey) {
        this.accessKey = accessKey;
    }

    public void setSecretKey(String secretKey) {
        this.secretKey = secretKey;
    }

    public void setBucketName(String bucketName) {
        this.bucketName = bucketName;
    }

    public void setRegion(String region) {
        this.region = region;
    }

    private MinioClient getMinioClient() {
        try {
            return MinioClient.builder()
                    .endpoint(endpoint)
                    .credentials(accessKey, secretKey)
                    .build();
        } catch (Exception e) {
            throw new RuntimeException("Failed to initialize MinIO client", e);
        }
    }

    @Override
    public void store(InputStream inputStream, long contentLength, String contentType, String keyName) {
        try {
            MinioClient minioClient = getMinioClient();
            if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            }
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(keyName)
                            .stream(inputStream, contentLength, -1)
                            .contentType(contentType)
                            .region(region)
                            .build());
        } catch (Exception e) {
            throw new RuntimeException("Failed to store file to MinIO", e);
        }
    }

    @Override
    public Stream<Path> loadAll() {
        try {
            MinioClient minioClient = getMinioClient();
            return StreamSupport.stream(
                    minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build()).spliterator(),
                    false)
                    .map(t -> {
                        try {
                            return t.<Item>get();
                        } catch (InvalidKeyException | ErrorResponseException | IllegalArgumentException
                                | InsufficientDataException | InternalException | InvalidResponseException
                                | NoSuchAlgorithmException | ServerException | XmlParserException | IOException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        return null;
                    })
                    .map(Item::objectName)
                    .map(Path::of);
        } catch (Exception e) {
            throw new RuntimeException("Failed to list files from MinIO", e);
        }
    }

    @Override
    public Path load(String keyName) {
        return Path.of(keyName);
    }

    @Override
    public Resource loadAsResource(String keyName) {
        try {
            MinioClient minioClient = getMinioClient();
            InputStream inputStream = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(keyName)
                            .build());
            return new InputStreamResource(inputStream);
        } catch (Exception e) {
            throw new RuntimeException("Failed to load file from MinIO", e);
        }
    }

    @Override
    public void delete(String keyName) {
        try {
            MinioClient minioClient = getMinioClient();
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName)
                            .object(keyName)
                            .build());
        } catch (Exception e) {
            throw new RuntimeException("Failed to delete file from MinIO", e);
        }
    }

    @Override
    public String generateUrl(String keyName) {
        return endpoint + "/" + bucketName + "/" + keyName;
    }
}
